﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Reflection;
using System.Collections;
using System.IO;
using System.Diagnostics.Contracts;

using King.Antlr3;
using King.Extensions;
using King.Text;
using King.Reflection;
using King.Language;

using Antlr.Runtime;
using Antlr.Runtime.Tree;

namespace King.Language.CSharp {

    public enum CSharpPreProcessorAstType {
        ActiveSection,
        InactiveSection,

        BooleanLiteral,

        OpenParenthesis,
        CloseParenthesis,
        Comma,
        DoubleAmperstand,
        DoubleBar,
        DoubleEqual,
        Exclamation,
        ExclamationEqual,

        SingleLineComment,
        DelimitedComment,

        Symbol,
        Undefine,
        Define,

        Conditional,
        If,
        Else,
        Elif,
        EndIf,

        Region,
        EndRegion,

        Line,
        LineDefault,
        LineHidden,

        Warning,
        Error,

        PragmaWarningDisable,
        PragmaWarningRestore,

        NewLine,
        Whitespace,
    }
    public enum CSharpPreProcessorExpressionType {
        Not,
        OrElse,
        AndAlso,
        Equal,
        NotEqual,
        Constant,
        Variable,
    }

    public struct CSharpPreProcessorToken {

        private AntlrToken m_token;
        private TokenTreeAdaptor m_adaptor;

        internal CSharpPreProcessorToken(TokenTreeAdaptor adaptor, AntlrToken token) {
            m_token = token;
            m_adaptor = adaptor;
        }

        internal bool IsDefault {
            get { return m_token.IsDefault; }
        }

        public int Type {
            get { return m_token.Type; }
        }

        public int Line {
            get { return m_token.Line; }
        }
        public int Column {
            get { return m_token.Column; }
        }
        public int Position {
            get { return m_token.Position; }
        }
        public int Length {
            get { return m_token.Length; }
        }
        public string Text {
            get { throw new NotImplementedException(); }
        }

        public CSharpPreProcessorToken StartToken {
            get { return new CSharpPreProcessorToken(m_adaptor, m_token.StartToken); }
        }
        public CSharpPreProcessorToken EndToken {
            get { return new CSharpPreProcessorToken(m_adaptor, m_token.EndToken); }
        }
        public IEnumerable<CSharpPreProcessorToken> Children() {
            var adaptor = m_adaptor;
            return m_token.Children(m_adaptor).Select(o => new CSharpPreProcessorToken(adaptor, o));
        }
    }

    public static class CSharpPreProcessorParser {

        public unsafe static TextReader PreProcess(TextReader code, IEnumerable<string> preProcessorSymbols = null) {

            var stream = new ANTLRReaderStream(code);

            // adapter
            int cBuffer = 256 * 256;
            byte* pBuffer = stackalloc byte[cBuffer];
            var adapter = new TokenTreeAdaptor(
                new Allocator(pBuffer, cBuffer), 
                tokenNames: AntlrCSharpPreProcessorParser.tokenNames, 
                stream: stream);

            // pre-process code
            var preProcessedAst = Parse(adapter, preProcessorSymbols);

            // gather active partitions
            //var treeStream = new MyCommonTreeNodeStream(adapter, preProcessedAst);
            //var walker = new AntlrCSharpPreProcessorWalker(treeStream);

            //typeof(AntlrCSharpPreProcessorWalker).Invoke("start", walker);
            //var activePartitions = walker.Tokens().ToList(o => new KeyValuePair<int, int>(o.StartToken.Index, o.EndToken.Index));

            //// strip inactive text
            //var preProcessedCode = new CSharpPreProcessedStream(code, activePartitions);

            //return preProcessedCode;
            throw new NotImplementedException();
        }
        public static TextReader PreProcess(string code, IEnumerable<string> preProcessorSymbols = null) {
            return PreProcess(new StringReader(code), preProcessorSymbols);
        }
        public static TextReader PreProcess(Stream stream, IEnumerable<string> preProcessorSymbols = null) {
            return PreProcess(new StreamReader(stream), preProcessorSymbols);
        }
        public static TextReader PreProcessFile(string file, IEnumerable<string> preProcessorSymbols = null) {
            return PreProcess(new StreamReader(file), preProcessorSymbols);
        }

        public unsafe static CSharpPreProcessorToken Parse(TextReader code, IEnumerable<string> preProcessorSymbols = null) {

            var stream = new ANTLRReaderStream(code);

            // adapter
            int cBuffer = 256 * 256;
            byte* pBuffer = stackalloc byte[cBuffer];
            var adapter = new TokenTreeAdaptor(
                new Allocator(pBuffer, cBuffer),
                tokenNames: AntlrCSharpPreProcessorParser.tokenNames, 
                stream: stream);

            // pre-process code
            var result = Parse(adapter, preProcessorSymbols);
            return result;
        }
        public static CSharpPreProcessorToken Parse(string code, IEnumerable<string> preProcessorSymbols = null) {
            return Parse(new StringReader(code), preProcessorSymbols);
        }
        public static CSharpPreProcessorToken Parse(Stream stream, IEnumerable<string> preProcessorSymbols = null) {
            return Parse(new StreamReader(stream), preProcessorSymbols);
        }
        //public static CSharpPreProcessorToken ParseFile(string file, IEnumerable<string> preProcessorSymbols = null) {
        //    return Parse(new FileStream(file), preProcessorSymbols);
        //}

        public unsafe static CSharpPreProcessorToken Parse(TokenTreeAdaptor adapter, IEnumerable<string> preProcessorSymbols = null) {
            var rule = "start";

            // activate pre-processor parser
            var lexer = new AntlrCSharpPreProcessorLexer(adapter.Stream);
            var tokens = new MyCommonTokenStream(lexer);
            var parser = new AntlrCSharpPreProcessorParser(tokens);

            // expose adapter to create tokens/trees
            parser.TreeAdaptor = adapter;
            lexer.TokenTreeAdapter = adapter;

            // set symbols
            parser.AddPreProcessorSymbols(preProcessorSymbols);

            // pre-process code
            var scope = (IAstRuleReturnScope)typeof(AntlrCSharpPreProcessorParser).Invoke(rule, parser);

            var nillableToken = (NillableToken)scope.Tree;
            var antlrToken = nillableToken.AntlrToken;
            var token = new CSharpPreProcessorToken(adapter, antlrToken);

            return token;
        }
    }
    internal class CSharpPreProcessedStream : TextReader {

        private TextReader m_reader;
        private long m_position;
        private IEnumerator<KeyValuePair<int, int>> m_activePartitions;

        public CSharpPreProcessedStream(TextReader reader, IEnumerable<KeyValuePair<int, int>> activePartitions) {
            m_reader = reader;
            m_position = -1;
            m_activePartitions = activePartitions.GetEnumerator();
        }

        private bool IsActive() {
            if (m_activePartitions == null)
                return false;

            while (m_position == -1 || m_activePartitions.Current.Value >= m_position) {

                if (!m_activePartitions.MoveNext()) {
                    m_activePartitions = null;
                    return false;
                }
            }

            return m_activePartitions.Current.Key <= m_position;
        }

        public override void Close() {
            m_reader.Close();
        }
        protected override void Dispose(bool disposing) {
            if (disposing)
                GC.SuppressFinalize(this);

            m_reader.Dispose();
        }

        public override int Peek() {
            var result = m_reader.Peek();

            if (result != -1 && !IsActive())
                result = (int)' ';

            return result;
        }
        public override int Read() {
            var result = Peek();

            m_position++;
            m_reader.Read();

            return result;
        }
    }

    public sealed class CSharpPreProcessorAstAttribute : Attribute {

        internal CSharpPreProcessorAstAttribute(params CSharpPreProcessorAstType[] types) {
        }
    }
    public abstract class CSharpPreProcessorAst {

        public unsafe static IEnumerable<CSharpPreProcessorAst> Parse(TextReader code, IEnumerable<string> preProcessorSymbols = null) {

            var stream = new ANTLRReaderStream(code);

            // adapter
            int cBuffer = 256 * 256;
            byte* pBuffer = stackalloc byte[cBuffer];
            var adapter = new TokenTreeAdaptor(
                new Allocator(pBuffer, cBuffer),
                tokenNames: AntlrCSharpPreProcessorParser.tokenNames, stream: stream);

            var token = CSharpPreProcessorParser.Parse(adapter, preProcessorSymbols);

            if (token.IsDefault)
                return Enumerable.Empty<CSharpPreProcessorAst>();

            //if (!token.IsNil)
            //    return Construct(token).ToEnumerable<CSharpPreProcessorAst>();

            //return token.Children().Select(o => Construct(o));
            throw new NotImplementedException();
        }
        public static IEnumerable<CSharpPreProcessorAst> Parse(string code, IEnumerable<string> preProcessorSymbols = null) {
            return Parse(new StringReader(code), preProcessorSymbols);
        }
        public static IEnumerable<CSharpPreProcessorAst> Parse(Stream stream, IEnumerable<string> preProcessorSymbols = null) {
            return Parse(new StreamReader(stream), preProcessorSymbols);
        }
        public static IEnumerable<CSharpPreProcessorAst> ParseFile(string file, IEnumerable<string> preProcessorSymbols = null) {
            return Parse(new StreamReader(file), preProcessorSymbols);
        }

        private static CSharpPreProcessorAst ConstructComments(CSharpPreProcessorToken token) {

            //var type = tree.Token.Type;

            //// comment delimited
            //if (type == CSharpLexer.DELIMITED_COMMENT)
            //    return new CSharpComment(Regex.Match(token.Text, @"^[/][*](?<comment>.*)[*][/]$").Get("comment"), true, token);

            // comment single-line
            //if (type == CSharpLexer.SINGLE_LINE_COMMENT)
            //    return new CSharpComment(Regex.Match(token.Text, @"^[/][/](?<comment>.*)$").Get("comment"), false, token);

            return null;
        }
        private static CSharpPreProcessorAst ConstructPreProcessor(CSharpPreProcessorToken token, IEnumerable<CSharpPreProcessorAst> children) {

            //var type = tree.Token.Type;
            //var parentType = tree.Parent == null ? 0 : tree.Parent.Type;

            //// #define
            //if (type == CSharpLexer.PP_DEFINE)
            //    return new CSharpPreProcessorDeclaration(true, children, token);

            //// #undef
            //else if (type == CSharpLexer.PP_UNDEFINE)
            //    return new CSharpPreProcessorDeclaration(false, children, token);

            //else if (type == CSharpLexer.PP_WARNING || type == CSharpLexer.PP_ERROR) {
            //    var message = Regex.Match(token.Text, @"^#(warning|error) (?<message>.*)$").Get("message");

            //    // #warning
            //    if (type == CSharpLexer.PP_WARNING)
            //        return new CSharpPreProcessorWarning(message, token);

            //    // #error
            //    else if (type == CSharpLexer.PP_ERROR)
            //        return new CSharpPreProcessorError(message, token);
            //}

            //// #pragma warning restore
            //else if (type == CSharpLexer.PP_PRAGMA_WARNING_RESTORE)
            //    return new CSharpPreProcessorPragma(true, children, token);

            //// #pragma warning disable
            //else if (type == CSharpLexer.PP_PRAGMA_WARNING_DISABLE)
            //    return new CSharpPreProcessorPragma(false, children, token);

            //// #region
            //else if (type == CSharpLexer.PP_REGION)
            //    return new CSharpPreProcessorRegion(Regex.Match(token.Text, @"^#region\s*(?<value>.*)$").Get("value"), children, token);

            //// #line default
            //else if (type == CSharpLexer.PP_LINE_DEFAULT)
            //    return new CSharpPreProcessorLine(false, token);

            //// #line hidden
            //else if (type == CSharpLexer.PP_LINE_HIDDEN)
            //    return new CSharpPreProcessorLine(true, token);

            //// #line
            //else if (type == CSharpLexer.PP_LINE) {
            //    var line = Regex.Match(token.Text, @"^#line\s*(?<line>\d+)\s*([""](?<file>.*)[""])?$");
            //    return new CSharpPreProcessorLine(line.Get<int>("line"), line.Get("file"), token);

            //    // <conditional>
            //} else if (type == CSharpLexer.PP_CONDITIONAL)
            //    return new CSharpPreProcessorConditional(children);

            //// #if
            //else if (type == CSharpLexer.PP_IF)
            //    return new CSharpPreProcessorIf(children, token);

            //// #elif
            //else if (type == CSharpLexer.PP_ELSE_IF)
            //    return new CSharpPreProcessorElif(children, token);

            //// #else
            //else if (type == CSharpLexer.PP_ELSE)
            //    return new CSharpPreProcessorElse(children, token);

            //// if-defed-out
            //else if (type == CSharpLexer.PP_INACTIVE_SECTION)
            //    return new CSharpPreProcessor(true, token);

            //// input
            //else if (type == CSharpLexer.PP_ACTIVE_SECTION)
            //    return new CSharpPreProcessor(false, token);

            //// pre-processor symbol
            //else if (type == CSharpLexer.CONDITIONAL_SYMBOL) {

            //    // declaration
            //    if (parentType == CSharpLexer.PP_DEFINE || parentType == CSharpLexer.PP_UNDEFINE)
            //        return new CSharpSymbol(token.Text, token);
            //}

            return null;
        }
        private static CSharpPreProcessorAst ConstructExpression(CSharpPreProcessorToken token, IEnumerable<CSharpPreProcessorAst> children) {

            //var type = tree.Token.Type;

            //// binary operator "&&"
            //if (type == CSharpLexer.DOUBLE_AMPERSTAND)
            //    return new CSharpBinaryExpression(CSharpExpressionType.AndAlso, children, token);

            //// binary operator "||"
            //else if (type == CSharpLexer.DOUBLE_BAR)
            //    return new CSharpBinaryExpression(CSharpExpressionType.OrElse, children, token);

            //// binary operator "=="
            //else if (type == CSharpLexer.DOUBLE_EQUAL)
            //    return new CSharpBinaryExpression(CSharpExpressionType.Equal, children, token);

            //// binary operator "!="
            //else if (type == CSharpLexer.EXCLAMATION_EQUAL)
            //    return new CSharpBinaryExpression(CSharpExpressionType.NotEqual, children, token);

            //// unary operator "!"
            //else if (type == CSharpLexer.EXCLAMATION)
            //    return new CSharpUnaryExpression(CSharpExpressionType.Not, children, token);

            //// true/false
            //else if (type == CSharpLexer.BOOLEAN_LITERAL)
            //    return new CSharpConstantExpression(bool.Parse(token.Text), token);

            //// true/false
            //else if (type == CSharpLexer.INTEGER)
            //    return new CSharpConstantExpression(int.Parse(token.Text), token);

            return null;
        }
        private static CSharpPreProcessorAst Construct(CSharpPreProcessorToken token) {

            //var children = token.GetChildren().Select(o => Construct(o));

            //CSharpPreProcessorCode node = null;

            //// pre-processor
            //if ((node = ConstructPreProcessor(token, children)) != null)
            //    return node;

            //// comment
            //else if ((node = ConstructComments(token)) != null)
            //    return node;

            //// expression
            //else if ((node = ConstructExpression(token, children)) != null)
            //    return node;

            //else if (type == CSharpLexer.CONDITIONAL_SYMBOL)
            //    return new CSharpVariableExpression(token.Text, token);

            //else
            throw new NotImplementedException();
        }

        private CSharpPreProcessorToken m_token;

        internal CSharpPreProcessorAst() {
        }
        internal CSharpPreProcessorAst(CSharpPreProcessorToken token) {
        }

        internal virtual void Initialize() {
        }

        internal void Set() {
            if (!m_token.IsDefault)
                throw new InvalidOperationException();
        }
        internal void Get() {
            Initialize();
        }

        public CSharpPreProcessorToken Token {
            get { return m_token; }
        }
    }

    [CSharpPreProcessorAstAttribute(
        CSharpPreProcessorAstType.Define, 
        CSharpPreProcessorAstType.Undefine)]
    public sealed class CSharpPreProcessorDeclaration : CSharpPreProcessorAst {

        private bool m_define;
        private CSharpPreProcessorSymbol m_symbol;

        public CSharpPreProcessorDeclaration(CSharpPreProcessorToken token) 
            : base(token) {
        }

        internal override void Initialize() {
            if (m_symbol != null)
                return;
        }

        public bool IsDefine {
            get { Get(); return m_define; }
            set { Set(); m_define = value; }
        }
        public CSharpPreProcessorSymbol Symbol {
            get { Get(); return m_symbol; }
            set { Set(); m_symbol = value; }
        }
    }

    [CSharpPreProcessorAstAttribute(CSharpPreProcessorAstType.Conditional)]
    public sealed class CSharpPreProcessorConditional : CSharpPreProcessorAst {

        private CSharpPreProcessorIf m_if;
        private IEnumerable<CSharpPreProcessorElif> m_elifs;
        private CSharpPreProcessorElse m_else;

        public CSharpPreProcessorConditional(CSharpPreProcessorToken token)
            : base(token) {
        }

        public CSharpPreProcessorIf If {
            get { Get(); return m_if; }
            set { Set(); m_if = value; }
        }
        public IEnumerable<CSharpPreProcessorElif> Elifs {
            get { Get(); return m_elifs; }
            set { Set(); m_elifs = value; }
        }
        public CSharpPreProcessorElse Else {
            get { Get(); return m_else; }
            set { Set(); m_else = value; }
        }
    }
    [CSharpPreProcessorAstAttribute(CSharpPreProcessorAstType.If)]
    public sealed class CSharpPreProcessorIf : CSharpPreProcessorAst {

        private CSharpPreProcessorExpression m_test;

        public CSharpPreProcessorIf(CSharpPreProcessorToken token)
            : base(token) {
        }

        public CSharpPreProcessorExpression Test {
            get { Get(); return m_test; }
            set { Set(); m_test = value; }
        }
    }
    [CSharpPreProcessorAstAttribute(CSharpPreProcessorAstType.Elif)]
    public sealed class CSharpPreProcessorElif : CSharpPreProcessorAst {

        private CSharpPreProcessorExpression m_test;

        public CSharpPreProcessorElif(CSharpPreProcessorToken token)
            : base(token) {
        }

        public CSharpPreProcessorExpression Test {
            get { Get(); return m_test; }
            set { Set(); m_test = value; }
        }
    }
    [CSharpPreProcessorAstAttribute(CSharpPreProcessorAstType.Else)]
    public sealed class CSharpPreProcessorElse : CSharpPreProcessorAst {

        public CSharpPreProcessorElse(CSharpPreProcessorToken token)
            : base(token) {
        }
    }

    [CSharpPreProcessorAstAttribute(
        CSharpPreProcessorAstType.ActiveSection, 
        CSharpPreProcessorAstType.InactiveSection)]
    public sealed class CSharpPreProcessorSection : CSharpPreProcessorAst {

        private bool m_skipped;

        public CSharpPreProcessorSection(CSharpPreProcessorToken token)
            : base(token) {
        }

        public bool Skipped {
            get { Get(); return m_skipped; }
            set { Set(); m_skipped = value; }
        }
        public string Text {
            get { Get(); throw new NotImplementedException(); }
            set { Set(); throw new NotImplementedException(); }
        }
    }
    [CSharpPreProcessorAstAttribute(
        CSharpPreProcessorAstType.PragmaWarningDisable, 
        CSharpPreProcessorAstType.PragmaWarningRestore)]
    public sealed class CSharpPreProcessorPragma : CSharpPreProcessorAst {

        private bool m_restore;
        private IEnumerable<int> m_warnings;

        public CSharpPreProcessorPragma(CSharpPreProcessorToken token)
            : base(token) {
        }

        public IEnumerable<int> Warnings {
            get { Get(); return m_warnings; }
            set { Set(); m_warnings = value; }
        }
        public bool Restore {
            get { Get(); return m_restore; }
            set { Set(); m_restore = value; }
        }
    }
    [CSharpPreProcessorAstAttribute(CSharpPreProcessorAstType.Warning)]
    public sealed class CSharpPreProcessorWarning : CSharpPreProcessorAst {

        private string m_message;

        public CSharpPreProcessorWarning(CSharpPreProcessorToken token)
            : base(token) {
        }

        public string Message {
            get { Get(); return m_message; }
            set { Set(); m_message = value; }
        }
    }
    [CSharpPreProcessorAstAttribute(CSharpPreProcessorAstType.Error)]
    public sealed class CSharpPreProcessorError : CSharpPreProcessorAst {

        private string m_message;

        public CSharpPreProcessorError(CSharpPreProcessorToken token)
            : base(token: token) {
        }

        public string Message {
            get { Get(); return m_message; }
            set { Set(); m_message = value; }
        }
    }
    [CSharpPreProcessorAstAttribute(CSharpPreProcessorAstType.Region)]
    public sealed class CSharpPreProcessorRegion : CSharpPreProcessorAst {

        private string m_value;

        public CSharpPreProcessorRegion(CSharpPreProcessorToken token)
            : base(token) {
        }

        public string Value {
            get { Get(); return m_value; }
            set { Set(); m_value = value; }
        }
    }
    [CSharpPreProcessorAstAttribute(
        CSharpPreProcessorAstType.Line, 
        CSharpPreProcessorAstType.LineDefault, 
        CSharpPreProcessorAstType.LineHidden)]
    public sealed class CSharpPreProcessorLine : CSharpPreProcessorAst {

        private int m_line;
        private string m_fileName;
        private bool m_hidden, m_default;

        public CSharpPreProcessorLine(CSharpPreProcessorToken token)
            : base(token) {
        }

        public bool Hidden {
            get { Get(); return m_hidden; }
            set { Set(); m_hidden = value; }
        }
        public bool Default {
            get { Get(); return m_default; }
            set { Set(); m_default = value; }
        }
        public string FileName {
            get { Get(); return m_fileName; }
            set { Set(); m_fileName = value; }
        }
        public int Line {
            get { Get(); return m_line; }
            set { Set(); m_line = value; }
        }
    }

    [CSharpPreProcessorAstAttribute(CSharpPreProcessorAstType.Symbol)]
    public sealed class CSharpPreProcessorSymbol : CSharpPreProcessorAst {

        public static implicit operator string(CSharpPreProcessorSymbol symbol) {
            return symbol.Symbol;
        }

        private string m_symbol;

        public CSharpPreProcessorSymbol(CSharpPreProcessorToken token)
            : base(token) {
        }

        public string Symbol {
            get { Get(); return m_symbol; }
            set { Set(); m_symbol = value; }
        }
    }
    [CSharpPreProcessorAstAttribute(
        CSharpPreProcessorAstType.DelimitedComment,
        CSharpPreProcessorAstType.SingleLineComment)]
    public sealed class CSharpPreProcessorComment : CSharpPreProcessorAst {

        private string m_comment;
        private bool m_delimited;

        public CSharpPreProcessorComment(CSharpPreProcessorToken token)
            : base(token) {
        }

        public string Value {
            get { Get(); return m_comment; }
            set { Set(); m_comment = value; }
        }
        public bool Delimited {
            get { Get(); return m_delimited; }
            set { Set(); m_delimited = value; }
        }
    }

    public abstract class CSharpPreProcessorExpression : CSharpPreProcessorAst {

        private CSharpPreProcessorExpressionType m_expressionType;

        protected CSharpPreProcessorExpression(CSharpPreProcessorToken token)
            : base(token) {
        }

        public CSharpPreProcessorExpressionType ExpressionType {
            get { Get(); return m_expressionType; }
            set { Set(); m_expressionType = value; }
        }
    }
    public sealed class CSharpPreProcessorVariableExpression : CSharpPreProcessorExpression {

        private string m_variable;

        public CSharpPreProcessorVariableExpression(CSharpPreProcessorToken token)
            : base(token) {
        }

        public string Symbol {
            get { Get(); return m_variable; }
            set { Set(); m_variable = value; }
        }
    }
    [CSharpPreProcessorAstAttribute(CSharpPreProcessorAstType.BooleanLiteral)]
    public sealed class CSharpPreProcessorConstantExpression : CSharpPreProcessorExpression {

        private object m_value;

        public CSharpPreProcessorConstantExpression(CSharpPreProcessorToken token)
            : base(token) {
        }

        public object Value {
            get { Get(); return m_value; }
            set { Set(); m_value = value; }
        }
    }
    [CSharpPreProcessorAstAttribute(
        CSharpPreProcessorAstType.DoubleAmperstand,
        CSharpPreProcessorAstType.DoubleBar,
        CSharpPreProcessorAstType.DoubleEqual,
        CSharpPreProcessorAstType.ExclamationEqual)]
    public sealed class CSharpPreProcessorBinaryExpression : CSharpPreProcessorExpression {

        private CSharpPreProcessorExpression m_left, m_right;

        public CSharpPreProcessorBinaryExpression(CSharpPreProcessorToken token)
            : base(token) {
        }

        public CSharpPreProcessorExpression Left {
            get { Get(); return m_left; }
            set { Set(); m_left = value; }
        }
        public CSharpPreProcessorExpression Right {
            get { Get(); return m_right; }
            set { Set(); m_right = value; }
        }

    }
    [CSharpPreProcessorAstAttribute(CSharpPreProcessorAstType.Exclamation)]
    public sealed class CSharpPreProcessorUnaryExpression : CSharpPreProcessorExpression {

        private CSharpPreProcessorExpression m_operand;

        public CSharpPreProcessorUnaryExpression(CSharpPreProcessorToken token)
            : base(token) {
        }

        public CSharpPreProcessorExpression Operand {
            get { Get(); return m_operand; }
            set { Set(); m_operand = value; }
        }
    }
}
